package net.qiqbframework.loadhanding;

import lombok.extern.slf4j.Slf4j;
import net.qiqbframework.common.Registration;
import net.qiqbframework.lifecycle.LifecycleHandlerRegister;
import net.qiqbframework.lifecycle.Phase;
import net.qiqbframework.messaging.*;
import net.qiqbframework.messaging.task.DefaultMessageTask;
import net.qiqbframework.messaging.task.MessageTask;
import net.qiqbframework.modelling.domain.AggregateModel;
import net.qiqbframework.modelling.domain.AggregateModelFactory;
import net.qiqbframework.modelling.domain.AggregateModellingException;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;

import static net.qiqbframework.common.BuilderUtils.assertNonNull;
import static net.qiqbframework.loadhanding.GenericLoadResultMessage.asLoadResultMessage;

@Slf4j
public class DefaultAggregateLoadBus implements AggregateLoadBus {
    /**
     * key ： aggregateNaming
     */
    private final ConcurrentMap<String, Map<String, MessageHandler<? super LoadMessage<?>>>> subscriptions = new ConcurrentHashMap<>();
    private final List<MessageHandlerInterceptor<? super LoadMessage<?>>> handlerInterceptors = new CopyOnWriteArrayList<>();

    private final AggregateModelFactory aggregateModelFactory;

    protected DefaultAggregateLoadBus(AggregateLoadBusImplBuilder builder) {
        builder.validate();
        this.aggregateModelFactory = builder.aggregateModelFactory;

    }


    @Override
    public <A> A load(@Nonnull LoadMessage<?> loadMessage) {

        Optional<MessageHandler<? super LoadMessage<?>>> optionalHandler = findLoadHandler(loadMessage.getAggregateName(), loadMessage.getBusinessIdName());

        if (optionalHandler.isPresent()) {

            MessageTask<LoadMessage<?>> messageMessageTask = DefaultMessageTask.startAndGet(loadMessage);

            InterceptorChain chain = new DefaultInterceptorChain<>(messageMessageTask, handlerInterceptors, optionalHandler.get());
            try {
                final ResultMessage<Object> loadResult = messageMessageTask.executeWithResult(chain::proceed, throwable -> false);
                return (A) loadResult.getPayload();
            } catch (Exception e) {
                throw e;
            }

        }
        return null;
    }

    @Override
    public <L, R> void load(@Nonnull LoadMessage<L> loadMessage, @Nonnull LoadCallback<L, R> loadCallback) {
        Optional<MessageHandler<? super LoadMessage<?>>> optionalHandler = findLoadHandler(loadMessage.getAggregateName(), loadMessage.getBusinessIdName());

        if (optionalHandler.isPresent()) {

            MessageTask<LoadMessage<?>> messageMessageTask = DefaultMessageTask.startAndGet(loadMessage);

            InterceptorChain chain = new DefaultInterceptorChain<>(messageMessageTask, handlerInterceptors, optionalHandler.get());
            final ResultMessage<Object> loadResult = messageMessageTask.executeWithResult(chain::proceed, throwable -> false);
            loadCallback.onResult(loadMessage, asLoadResultMessage(loadResult.getPayload()));

        } else {
            LoadHandlerNotFoundException exception = new LoadHandlerNotFoundException("No handler was subscribed for load [%s].".formatted(loadMessage.getAggregateName()));
            loadCallback.onResult(loadMessage, asLoadResultMessage(exception));
        }
    }


    @Override
    public Registration subscribe(@Nonnull String aggregateName, @Nonnull String bizIdentifierName, @Nonnull MessageHandler<? super LoadMessage<?>> handler) {
        assertNonNull(handler, "handler may not be null");
        Map<String, MessageHandler<? super LoadMessage<?>>> bizIdentifierMap = subscriptions.computeIfAbsent(aggregateName, k -> new ConcurrentHashMap<>());

        bizIdentifierMap.compute(bizIdentifierName, (k, existingHandler) -> {
            if (existingHandler == null) {
                return handler;
            } else {
                throw new CreateLoadException("聚合[%s]的业务ID[%s]定义了多个".formatted(aggregateName, bizIdentifierName));
            }
        });
        return () -> subscriptions.remove(aggregateName, bizIdentifierMap);
    }


    public Optional<MessageHandler<? super LoadMessage<?>>> findLoadHandler(String aggregateName, @Nonnull String businessIdName) {
        if (subscriptions.get(aggregateName) == null) {
            return Optional.empty();
        }

        return Optional.ofNullable(subscriptions.get(aggregateName).get(businessIdName));
    }


    public static AggregateLoadBusImplBuilder builder() {
        return new AggregateLoadBusImplBuilder();
    }

    @Override
    public void registerLifecycleHandler(@Nonnull LifecycleHandlerRegister register) {

        register.onStart(
                Phase.LOCAL_MESSAGE_HANDLER_VERIFY,
                () -> {
                    // 验证每个聚合对象的biz对应一个加载器
                    for (String aggregateNaming : aggregateModelFactory.getAllAggregateNaming()) {
                        final Optional<AggregateModel<Object>> aggregateModel = aggregateModelFactory.getModel(aggregateNaming);
                        if (aggregateModel.isEmpty()) {
                            // 理论上不会出现这种情况
                            throw new AggregateModellingException("未初始化");
                        }


                    }
                }
        );
    }
}
